/*
 * Copyright (c) 2014-2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/*
 * ======== netwifi.c ========
 *
 * WiFi network functions
 *
 */

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

#include <ti/drivers/net/wifi/simplelink.h>
#include <ti/net/wac/WAC.h>

#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#include <unistd.h>

#include "netwifi.h"

#define NUMDEVICEIDBYTES     6
#define POLLINTERVAL        100 /* milliseconds */
#define WACPORTNUM          2222
#define MANUFACTURER        "Texas Instruments"
#define MODEL               "ABCDE"
#define SERIALNUM           "1234567"

static volatile bool ipAcquired = false;
timer_t timer;
sigevent sev;
struct itimerspec value;
sem_t timerSem;
pthread_t wacTimeoutThread;
bool runWac = false;
bool waitForIPAquired = false;

static void connectionTimeoutFxn(sigval arg)
{

    if(!ipAcquired) {
        sem_post(&timerSem);
        runWac = true;
    }
}

static void* wacTimeoutTaskFxn(void* arg)
{

    sleep(60 * 30);
    WAC_stop();

    pthread_exit(wacTimeoutThread);

    return (NULL);
}


#if 0
/*------------------------------------------------------------------------------
 *  Event Handlers that developers can implement to suit their applications.
 *----------------------------------------------------------------------------*/

void SimpleLinkGeneralEventHandler(SlDeviceEvent_t *sockEvent) {}

void SimpleLinkSockEventHandler(SlSockEvent_t *sockEvent) {}

void SimpleLinkFatalErrorEventHandler(SlDeviceFatal_t *pSlFatalErrorEvent) {}

void slcb_NetAppRequestHdlr(SlNetAppRequest_t *pNetAppRequest,
SlNetAppResponse_t *pNetAppResponse) {}

void slcb_NetAppRequestMemFree (_u8 *buffer) {}
/*
 *  ======== SimpleLinkWlanEventHandler ========
 */
void SimpleLinkWlanEventHandler(SlWlanEvent_t *pArgs){}

/*
 *  ======== SimpleLinkNetAppEventHandler ========
 */
void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *pArgs)
{
    switch (pArgs->Id) {
        case SL_NETAPP_EVENT_IPV4_ACQUIRED:
            if(waitForIPAquired) {
                sem_post(&timerSem);
            }
            waitForIPAquired = false;
            ipAcquired = true;
            break;

        default:
            break;
   }
}


void SimpleLinkSocketTriggerEventHandler(SlSockTriggerEvent_t *arg)
{

}
/*
 *  ======== SimpleLinkHttpServerCallback ========
 */
void
SimpleLinkHttpServerEventHandler(SlNetAppHttpServerEvent_t *pHttpServerEvent,
                             SlNetAppHttpServerResponse_t *pHttpServerResponse)
                             {}

#endif


/*
 *  ======== NetWiFi_waitForNetwork ========
 *  Wait for Network initialization to complete
 */

int NetWiFi_waitForNetwork(char *deviceName,
                           unsigned char *deviceID,
                           NetWiFi_pollWACFxn pollFxn,
                           int timeout, unsigned long *ipAddr)
{
    SlNetCfgIpV4Args_t ipV4 = {0};
    uint16_t len = sizeof(ipV4);
    uint16_t dhcpBits;
    unsigned char IfBitmap = 0;
    volatile uint32_t status;
    WAC_memBlock *wacBuffer;
    uint8_t deviceNameLen = strlen(deviceName);
    pthread_attr_t taskParams;
    pthread_attr_init(&taskParams);
    sem_init(&timerSem, 0, 0);

    /*
     *  Run poll fxn every 0.5s until timeout is reached
     *  If pollFxn returns NETWIFI_RUNWAC, then run WAC
     */
    if (pollFxn) {
        while (timeout > 0) {
            if (pollFxn() == NETWIFI_RUNWAC) {
                runWac = true;
                break;
            }
            usleep(POLLINTERVAL * 1000);
            timeout = timeout - POLLINTERVAL;
        }
    }

    sl_Start(NULL, NULL, NULL);

    sl_NetAppSet(SL_NETAPP_DEVICE_ID, SL_NETAPP_DEVICE_URN, deviceNameLen,
                    (_u8 *)deviceName);

    sl_WlanSetMode(ROLE_STA);
    sl_NetCfgSet(SL_NETCFG_IPV4_STA_ADDR_MODE, SL_NETCFG_ADDR_DHCP, 0, 0);
    sl_NetCfgSet(SL_NETCFG_IPV4_STA_ADDR_MODE, SL_NETCFG_ADDR_DHCP_LLA, 0, 0);

    IfBitmap = SL_NETCFG_IF_IPV6_STA_LOCAL;
    sl_NetCfgSet(SL_NETCFG_IF, SL_NETCFG_IF_STATE, sizeof(IfBitmap),
                    &IfBitmap);
    sl_NetCfgSet(SL_NETCFG_IPV6_ADDR_LOCAL, SL_NETCFG_ADDR_STATEFUL, 0, 0);

    IfBitmap = SL_NETCFG_IF_IPV6_STA_GLOBAL;
    sl_NetCfgSet(SL_NETCFG_IF, SL_NETCFG_IF_STATE, sizeof(IfBitmap),
                    &IfBitmap);
    sl_NetCfgSet(SL_NETCFG_IPV6_ADDR_GLOBAL, SL_NETCFG_ADDR_STATEFUL, 0, 0);

    sl_WlanPolicySet(SL_WLAN_POLICY_CONNECTION,
                    SL_WLAN_CONNECTION_POLICY(1, 0, 0, 0), NULL, 0);

    if (runWac) {
        sl_WlanProfileDel(SL_WLAN_DEL_ALL_PROFILES);
    }

    waitForIPAquired = true;
    ipAcquired = false;

    sl_Stop(200);
    sl_Start(NULL, NULL, NULL);

    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_notify_function = &connectionTimeoutFxn;
    timer_create(CLOCK_MONOTONIC, &sev, &timer);

    value.it_interval.tv_sec = 0;
    value.it_interval.tv_nsec = 0;
    value.it_value.tv_sec = 10;
    value.it_value.tv_nsec = 0;
    timer_settime(timer, 0, &value, NULL);

    sem_wait(&timerSem);

    timer_delete(timer);

    if (runWac) {
        wacBuffer = (WAC_memBlock *)malloc(sizeof(WAC_memBlock));
        WAC_setUserCapabilities wacCapabilities;
        WAC_setListeningPort wacListeningPort;
        wacListeningPort.listeningPort = WACPORTNUM;
        wacCapabilities.userCaps = (WAC_CAP_FLAG_USE_WPS |
                                        WAC_CAP_FLAG_HOMEKIT);

        WAC_init(WAC_RUN_BLOCKING, wacBuffer, NULL);
        WAC_set(WAC_OPT_MANUFACTURER, MANUFACTURER, strlen(MANUFACTURER), 0);
        WAC_set(WAC_OPT_DEVICE_MODEL, MODEL, strlen(MODEL), 0);
        WAC_set(WAC_OPT_SERIAL_NUMBER, SERIALNUM, strlen(SERIALNUM), 0);
        WAC_set(WAC_OPT_DEVICE_CAPABILITIES, &wacCapabilities,
                            sizeof(WAC_setUserCapabilities), 0);
        WAC_set(WAC_OPT_LISTENING_PORT,&wacListeningPort,
                            sizeof(WAC_setListeningPort), 0);
        WAC_set(WAC_OPT_DEVICE_ID, deviceID, NUMDEVICEIDBYTES, 0);

        pthread_create(&wacTimeoutThread, &taskParams, wacTimeoutTaskFxn,
                        NULL);

        status = WAC_run();
        free(wacBuffer);

        if (status != WAC_STATE_DONE) {
            return (NETWIFI_ERROR);
        }
    }

    if (ipAcquired) {
        sl_NetAppGet(SL_NETAPP_DEVICE_ID, SL_NETAPP_DEVICE_URN, &deviceNameLen,
                    (unsigned char *)deviceName);
        sl_NetCfgGet(SL_NETCFG_IPV4_STA_ADDR_MODE, &dhcpBits, &len,
            (unsigned char *)&ipV4);
        *ipAddr = ipV4.Ip;

        return (NETWIFI_SUCCESS);
    }
    else {
        return (NETWIFI_ERROR);
    }
}
